package com.xiaotu.handler;

import com.xiaotu.cache.BindPhoneAndroidDeviceIdCache;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

/**
 * 全局监控
 *
 * @author zhangxiao
 * @create 2022-05-25 14:30
 */
@Slf4j
@ServerEndpoint("/monitorSocket/{phone}/{androidDeviceId}")
@Component
@Data
@RequiredArgsConstructor
public class MonitorHandler {

    private Session session;

    private String phone;

    private String androidDeviceId;

    /**
     * 连接建立成功调用的方法
     *
     * @param session 链接session
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("phone") String phone, @PathParam("androidDeviceId") String androidDeviceId) {
        this.session = session;
        this.phone = phone;
        this.androidDeviceId = androidDeviceId;
        BindPhoneAndroidDeviceIdCache.bindPhoneAndroidDeviceId(phone, this);
        log.debug("链接成功，手机号：{}，当前 android 设备ID：{}", phone, androidDeviceId);
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 消息内容
     * @param session session
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.debug("手机号：{}，当前 android 设备ID：{}，当前消息：{}", this.phone, this.androidDeviceId, message);
        if (message.equals("在吗"))
            push("我在", session);
    }

    /**
     * 发生异常时的处理
     *
     * @param session   客户端session
     * @param throwable session
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        if (this.session != null && this.session.isOpen()) {
            log.error("websocket连接onError。inputSession：{}-localSession：{}", session.getId(), this.toString(), throwable);
            this.close(session);
        } else {
            log.debug("已经关闭的websocket连接发生异常！inputSession：{}-localSession：{}", session.getId(), this.toString(), throwable);
        }
        //移除绑定
        BindPhoneAndroidDeviceIdCache.unBindPhoneAndroidDeviceId(phone, this);
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        log.debug("websocket连接onClose。{}", this);
        //移除绑定
        BindPhoneAndroidDeviceIdCache.unBindPhoneAndroidDeviceId(phone, this);
        //然后关闭
        close(session);
    }

    /**
     * 推送消息
     *
     * @param message
     * @param session
     */
    public void push(String message, Session session) {
        if (StringUtils.isEmpty(message)) {
            throw new RuntimeException("message must not be null, message=" + message);
        }
        if (!session.isOpen()) {
            this.close(session);
            throw new RuntimeException("session is closed, message=" + message);
        }
        //发送消息
        this.sendMessage(message, session);
    }

    /**
     * 关闭session连接
     */
    private void close(Session session) {
        if (session == null) {
            log.debug("websocket连接关闭完成。{}", this.toString());
            return;
        }
        try {
            if (session.isOpen()) {
                session.close();
            }
            log.info("连接已经关闭");
        } catch (IOException e) {
            log.error("websocket连接关闭时异常。{}", this.toString(), e);
        }
    }

    /**
     * 推送消息给客户端
     *
     * @param message 消息内容
     */
    private void sendMessage(String message, Session session) {
        try {
            //使用同步块和同步方法发送。看是否能防止产生IllegalStateException异常
            //modify by caoshuo at 200506
            synchronized (this) {
                session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            log.error("websocket连接发送客户端发送消息时异常！{}-{}", message, this.toString(), e);
        }
    }

}
